/*
 * File Name：MultipartEntity.java
 * Copyright：Copyright 2008-2015 CiWong.Inc. All Rights Reserved.
 * Description：MultipartEntity.java
 * Modify By：Administrator
 * Modify Date：2015年4月15日
 * Modify Type：Add
 */
package com.ciwong.libs.utils.volley;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.message.BasicHeader;

import android.text.TextUtils;

/**
 * Volley文件上传实体类
 * 
 * @author Administrator
 * @version ciwong v.1.0 2015年4月15日
 * @since ciwong v.1.0
 */
public class MultipartEntity implements HttpEntity
{
    /**
     * 两横线
     */
    private final String TWO_HYPHEN = "--";

    /**
     * 分隔符
     */
    private String BOUNDARY = UUID.randomUUID().toString();
    
    /**
     * 换行符
     */
    private final String NEW_LINE_STR = "\r\n";

    private final String CONTENT_TYPE = "Content-Type: ";

    private final String CONTENT_DISPOSITION = "Content-Disposition: ";

    /**
     * 文本参数和字符集
     */
    private final String TYPE_TEXT_CHARSET = "text/plain; charset=UTF-8";

    /**
     * 字节流参数
     */
    private final String TYPE_OCTET_STREAM = "text/plain";

    /**
     * 二进制参数
     */
    private final byte[] BINARY_ENCODING = "Content-Transfer-Encoding: binary\r\n\r\n"
            .getBytes();

    /**
     * 文本参数
     */
    private final byte[] BIT_ENCODING = "Content-Transfer-Encoding: 8bit\r\n\r\n"
            .getBytes();

    /**
     * 输出流
     */
    private ByteArrayOutputStream mOutputStream = new ByteArrayOutputStream();

    /**
     * 参数开头的分隔符
     * 
     * @throws IOException
     */
    private void writeFirstBoundary() throws IOException
    {
        mOutputStream.write((TWO_HYPHEN + BOUNDARY + NEW_LINE_STR).getBytes());
    }

    /**
     * 添加文本参数
     * 
     * @param key
     * @param value
     */
    public void addStringPart(final String paramName, final String value)
    {
        writeToOutputStream(paramName, value.getBytes(), TYPE_TEXT_CHARSET,
                BIT_ENCODING, "");
    }

    /**
     * 将数据写入到输出流中
     * 
     * @param key
     * @param rawData
     * @param type
     * @param encodingBytes
     * @param fileName
     */
    private void writeToOutputStream(String paramName, byte[] rawData,
            String type, byte[] encodingBytes, String fileName)
    {
        try
        {
            writeFirstBoundary();
            mOutputStream
                    .write((CONTENT_TYPE + type + NEW_LINE_STR).getBytes());
            mOutputStream
                    .write(getContentDispositionBytes(paramName, fileName));
            mOutputStream.write(encodingBytes);
            mOutputStream.write(rawData);
            mOutputStream.write(NEW_LINE_STR.getBytes());
        }
        catch (final IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 添加二进制参数, 例如Bitmap的字节流参数
     * 
     * @param key
     * @param rawData
     */
    public void addBinaryPart(String paramName, final byte[] rawData)
    {
        writeToOutputStream(paramName, rawData, TYPE_OCTET_STREAM,
                BINARY_ENCODING, "no-file");
    }

    /**
     * 添加文件参数,可以实现文件上传功能
     * 
     * @param key
     * @param file
     */
    public void addFilePart(final String key, final File file)
    {
        InputStream fin = null;
        try
        {
            fin = new FileInputStream(file);
            writeFirstBoundary();
            final String type = CONTENT_TYPE + TYPE_OCTET_STREAM + NEW_LINE_STR;
            mOutputStream
                    .write(getContentDispositionBytes(key, file.getName()));
            mOutputStream.write(type.getBytes());
            mOutputStream.write(BINARY_ENCODING);

            final byte[] tmp = new byte[4096];
            int len = 0;
            while ((len = fin.read(tmp)) != -1)
            {
                mOutputStream.write(tmp, 0, len);
            }
            mOutputStream.write(NEW_LINE_STR.getBytes());
            mOutputStream.flush();
        }
        catch (final IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            closeSilently(fin);
        }
    }
    
    public void addFilePart(byte[] bytes)
    {
        try
        {
            mOutputStream.write(bytes);
            mOutputStream.flush();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally
        {
            try
            {
                mOutputStream.close();
            }
            catch (IOException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    private void closeSilently(Closeable closeable)
    {
        try
        {
            if (closeable != null)
            {
                closeable.close();
            }
        }
        catch (final IOException e)
        {
            e.printStackTrace();
        }
    }

    private byte[] getContentDispositionBytes(String paramName, String fileName)
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(CONTENT_DISPOSITION + "form-data; name=\""
                + paramName + "\"");
        // 文本参数没有filename参数,设置为空即可
        if (!TextUtils.isEmpty(fileName))
        {
            stringBuilder.append("; filename=\"" + fileName + "\"");
        }

        return stringBuilder.append(NEW_LINE_STR).toString().getBytes();
    }

    @Override
    public long getContentLength()
    {
        return mOutputStream.toByteArray().length;
    }

    @Override
    public Header getContentType()
    {
        return new BasicHeader("Content-Type", "application/octet-stream");
    }

    @Override
    public boolean isChunked()
    {
        return false;
    }

    @Override
    public boolean isRepeatable()
    {
        return false;
    }

    @Override
    public boolean isStreaming()
    {
        return false;
    }

    @Override
    public void writeTo(final OutputStream outstream) throws IOException
    {
        // 参数最末尾的结束符
//        final String endString = TWO_HYPHEN + BOUNDARY + TWO_HYPHEN
//                + NEW_LINE_STR;
        // 写入结束符
//        mOutputStream.write(endString.getBytes());
        //
        outstream.write(mOutputStream.toByteArray());
    }

    @Override
    public Header getContentEncoding()
    {
        return null;
    }

    @Override
    public void consumeContent() throws IOException,
            UnsupportedOperationException
    {
        if (isStreaming())
        {
            throw new UnsupportedOperationException(
                    "Streaming entity does not implement #consumeContent()");
        }
    }

    @Override
    public InputStream getContent()
    {
        return new ByteArrayInputStream(mOutputStream.toByteArray());
    }
}
